package mspbots.data.manage.core.client;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.log4j.Log4j2;
import mspbots.data.manage.annotation.ErrorResponse;
import mspbots.data.manage.annotation.RequestApiException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * mspbots.data.manage.core.client.AbstractDefaultClient
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/11/12
 */
@Log4j2
public abstract class AbstractDefaultClient implements BaseClient {

    protected WebClient webClient;

    protected AbstractDefaultClient(WebClient webClient) {
        this.webClient = webClient;
    }

    @Override
    public <T> Mono<Page<T>> search(String path, Object body, Class<T> responseType) {
        return this.webClient.post()
                .uri(path)
                .accept(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse -> clientResponse.bodyToMono(JsonNode.class)
                        .map(RequestApiException::withMsg))
                .bodyToMono(new ParameterizedTypeReference<>() {
                });
    }

    @Override
    public <T> Flux<T> post(String path, Object body, Class<T> responseType) {
        return this.webClient.post()
                .uri(path)
                .accept(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse -> clientResponse.bodyToMono(JsonNode.class)
                        .map(RequestApiException::withMsg))
                .bodyToFlux(responseType);
    }


    @Override
    public <T> Mono<Page<T>> find(String path, Map<String, Object> params, Class<T> responseType) {
        return this.webClient.get()
                .uri(uriBuilder -> uriBuilder.path(path)
                        .queryParams(ClientUtils.mapToQueryParams(params)).build())
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse -> clientResponse.bodyToMono(JsonNode.class)
                        .map(RequestApiException::withMsg))
                .bodyToMono(new ParameterizedTypeReference<>() {
                });
    }

    @Override
    public <T> Flux<T> get(String path, Map<String, Object> params, Class<T> responseType) {
        return this.webClient.get()
                .uri(uriBuilder -> uriBuilder.path(path).queryParams(ClientUtils.mapToQueryParams(params)).build())
                .accept(MediaType.APPLICATION_JSON)
                .headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_JSON))
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse -> {
                    if(clientResponse.statusCode()==HttpStatus.SERVICE_UNAVAILABLE){
                        ErrorResponse errorResponse=ErrorResponse.withErrors("The path ["+path+"] server is not responding. " +
                                "Please contact your administrator.");
                        errorResponse.setStatus(HttpStatus.SERVICE_UNAVAILABLE);
                        errorResponse.setMessage("Remote Service Unavailable");
                        return Mono.error(RequestApiException.withMsg(errorResponse));
                    }
                    return clientResponse.bodyToMono(JsonNode.class)
                            .map(RequestApiException::withMsg);
                })
                .bodyToFlux(responseType);
    }


}
